#pragma once

#include <iostream>
#include <string>
#include "Log.hpp"
#include "Socket.hpp"
#include <memory>
#include <poll.h>

using namespace SocketModule;
using namespace LogModule;

// #define NUM sizeof(fd_set)*8 // FD_SETSIZE使用系统定义的最大fd数量
const int gdefaultfd = -1;

#define MAX 4096

// 最开始的时候，tcpserver只有一个listensockfd
class PollServer
{
public:
    PollServer(int port)
    : _port(port),
     _listen_socket(std::make_unique<TcpSocket>()),
     _isrunning(false)
    {
    }

    void Init()
    {
        _listen_socket->BuildTcpSocketMethod(_port);
        for(int i = 0; i < MAX; i++)
        {
            _fds[i].fd = gdefaultfd;
            _fds[i].events = 0;
            _fds[i].revents = 0;
        }
        // 先把读文件描述符加入 poll
        _fds[0].fd = _listen_socket->Fd();
        _fds[0].events |= POLLIN;
        // _fds[0].revents;
    }

    void Loop()
    {
        int timeout = -1; // 设置为 0 非阻塞等待一直 time out， 设置为 -1 永久阻塞 直到新信息的到来
        _isrunning = true;
        while(_isrunning)
        {
            // 3. 不能让 accepet 来阻塞检测新连接到来，应该用 select 负责进行就绪事件的检测
                // 用户告诉内核，你要帮我关心 &rfds 中的哪些 fd 有事件就绪，然后内核帮你处理
            int n = poll(_fds, MAX, timeout); 
            switch (n)
            {
            case 0:
                std::cout << "time out..." << std::endl; 
                break;
            case -1:
                // perror("poll"); // 注释掉这个就不会有问题了
                break;
            default:
                // 有事件就绪 --> 直接在内核的 revents 上设置了
                // rfds: 内核告诉用户，关心 rfds 中的哪些 fd 有事件就绪
                Dispatcher(); // 把已经就绪的sockfd，派发给指定的模块
                TestFd();
                break;
            } 
        }
        _isrunning = false;
    }

    void Accepter() // 连接获取
    {
        // 有新连接到来 1. 获取新连接
        InetAddr client;
        int newfd = _listen_socket->Accepter(&client); // 这里就不会被阻塞了，因为这里 select 已经告诉我们 listensockfd 就绪了，只需要执行拷贝即可 
        
        // 2. 处理新连接
        if(newfd < 0) return ;
        else
        {
            std::cout << "获得了应该新的连接: " << newfd << " client info: " << client.Addr() << std::endl;
            // recv() 读事件是否就绪, 我们并不清楚! newfd 托管给select，select 帮我进行关心新的 sockfd 上面的读事件就绪
            // 怎么把新的 newfd 托管给 poll，让 poll 帮我去关心newfd 上的读事件 --> +到辅助数组
            int pos = -1;
            for(int j = 0; j < MAX; j++)
            {
                if(_fds[j].fd == gdefaultfd)
                {
                    pos = j; // 找到一个暂未初始化的 
                    break;
                }
            }

            if(pos == -1)
            {
                // 如果用指针的话，可以进行自动扩容
                LOG(LogLevel::ERROR) << "服务器已经满了....";
                close(newfd);
            }
            else
            {
                _fds[pos].fd = newfd;
                _fds[pos].events = POLLIN;
            }
        }
    }

    void Recver(int who)
    {
        // 合法的 就绪的 fd
        // 这里的 recv 不对 不完善 --> 必须要有协议
        char buf[1024];
        ssize_t n = recv(_fds[who].fd, buf, sizeof(buf) - 1, 0); // 会不会阻塞，就绪了
        if(n > 0)
        {
            buf[n] = 0;
            std::cout << "client# " << buf << std::endl;
            // 把读到的信息 回显回去
            std::string message = "echo# ";
            message += buf;
            send(_fds[who].fd, message.c_str(), message.size(), 0);
        }
        else if(n == 0)
        { 
            LOG(LogLevel::DEBUG) << "客户端退出, sockfd: " << _fds[who].fd;
            close(_fds[who].fd);
            _fds[who].fd = gdefaultfd;
            _fds[who].events = _fds[who].revents = 0;
        }
        else
        {
            LOG(LogLevel::DEBUG) << "客户端读取错误, sockfd: " << _fds[who].fd ;
            close(_fds[who].fd);
            _fds[who].fd = gdefaultfd;
            _fds[who].events = _fds[who].revents = 0;
        }
    }

    void Dispatcher() 
    {
        for(int i = 0; i < MAX; i++)
        {
            if(_fds[i].fd == gdefaultfd) continue;
            // 此时文件描述符一定合法

            if(_fds[i].fd == _listen_socket->Fd()){ // 该 fd 是 linstenfd
                if (_fds[i].revents & POLLIN) // listen事件就绪了
                {
                    Accepter();
                }
            }
            else{ 
                if(_fds[i].revents & POLLIN) Recver(i); // 读事件就绪
                // else if(_fds[i].revents & POLLOUT) 
                // {
                //     // 写事件就绪
                // }
            }
        }   
    }

    void TestFd()
    {
        std::cout << "pollfd: ";
        for(int i = 0; i < MAX; i++)
        {
            if(_fds[i].fd == gdefaultfd) continue;
            std::cout << _fds[i].fd << "[" <<Events2Str( _fds[i].events) << "]";
        }
        std::cout << "\n";
    }

    std::string Events2Str(short events)
    {
        std::string s = ((events & POLLIN) ? "EPOLLIN" : "");
        s += ((events & POLLOUT) ? "POLLOUT" : "");
        return s;
    }
    ~PollServer()
    {}

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listen_socket;
    bool _isrunning;
    struct pollfd _fds[MAX];
    // struct pollfd *_fds; 以后可以用指针 进行 malloc 就行
};


